package Leetcode100Hot;

import org.junit.Test;

import java.util.HashMap;

/*
和为 K 的子数组
给你一个整数数组 nums 和一个整数 k ，请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。

示例 1：
输入：nums = [1,1,1], k = 2
输出：2
示例 2：
输入：nums = [1,2,3], k = 3
输出：2

提示：
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
 */
public class _64和为K的子数组 {

    @Test
    public void test() {
        System.out.println(subarraySum(new int[]{1, 2, 3}, 3));
    }

    //前缀和
    //ps: 虽然用了前缀和,但是思路和枚举差不多
    //优化：利用哈希表 + 数学
    //O(n ^ 2)
    //AC 1585ms
    public int subarraySum(int[] nums, int k) {
        int[] preSum = new int[nums.length + 1];
        preSum[0] = 0;
        for (int i = 0; i < nums.length; i++) {
            preSum[i + 1] = preSum[i] + nums[i];
        }
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
            for (int j = i; j >= 0; j--){
                if (preSum[i + 1] - preSum[j] == k){
                    count++;
                }
            }
        }
        return count;
    }

    //官解：方法一：枚举
    /*
    作者：力扣官方题解
    链接：https://leetcode.cn/problems/subarray-sum-equals-k/solutions/238572/he-wei-kde-zi-shu-zu-by-leetcode-solution/
     */
    public class Solution {
        public int subarraySum(int[] nums, int k) {
            int count = 0;
            for (int start = 0; start < nums.length; ++start) {
                int sum = 0;
                for (int end = start; end >= 0; --end) {
                    sum += nums[end];
                    if (sum == k) {
                        count++;
                    }
                }
            }
            return count;
        }
    }


    //官解：方法二：前缀和 + 哈希表优化
    //核心优化：
    //我们建立哈希表 mp，以和为键，出现次数为对应的值，记录 pre[i] 出现的次数,
    //从左往右边更新 mp 边计算答案，那么以 i 结尾的答案 mp[pre[i]−k] 即可在 O(1) 时间内得到。
    //优雅
    //O(n) O(n)
    /*
    作者：力扣官方题解
    链接：https://leetcode.cn/problems/subarray-sum-equals-k/solutions/238572/he-wei-kde-zi-shu-zu-by-leetcode-solution/
     */
    public class Solution2 {
        public int subarraySum(int[] nums, int k) {
            int count = 0, pre = 0;
            HashMap<Integer, Integer> mp = new HashMap<>();
            mp.put(0, 1);
            for (int i = 0; i < nums.length; i++) {
                pre += nums[i];
                if (mp.containsKey(pre - k)) {
                    count += mp.get(pre - k);
                }
                mp.put(pre, mp.getOrDefault(pre, 0) + 1);
            }
            return count;
        }
    }

}
